package com.shou.types.utils;

import com.shou.types.exception.ExternalException;
import com.shou.types.common.*;

import java.util.Base64;

import javax.crypto.Cipher;
import java.security.*;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
/**
 * ClassName:RSAUtil
 * Project:SmartPark
 * Package: com.shou.types.utils
 * Description: RSA非对称加密算法工具类
 * 两种方式获取公私钥，手动生成map和使用类加载时载入的map。
 *
 * @Author Nemo
 * @Create 2025/3/24 11:13
 * @Version 1.0
 */

public class RSAUtil {
    /**
     * RSA算法
     */
    private static final String RSA_ALGORITHM = "RSA";

    /**
     * 公私钥长度
     */
    private static final Integer KEY_LENGTH = 2048;

    /**
     * RSA算法公钥
     */
    private static final String RSA_PUBLIC_KEY = "RSAPublicKey";

    /**
     * RSA算法私钥
     */
    private static final String RSA_PRIVATE_KEY = "RSAPrivateKey";


    /**
     * 数据加密公钥
     */
    private static String PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApgT80HYrAxaYsp973uFFJEbs931S+8ELCHYwDl9T56u1QsjylwiGkkfXUyMe0pYK+I2RRl5iimRDXYy5h7IoD8Q7dPcUeCfiBWjpfXY/fqQsvFZ6hl64p0E0PLSUugttCHRxiLnpsh3VPSjdh9X59uz7y6FKeqb7IWEpN5FbAd+mjJcvmkHjXiLcgcnnzOrWjrFn2GrpRSweTKcnKY5RXZZC9/iiGYDLAi2mH+/8k+mJ3K+MvB8W2klQrp2SbgohvIbyczQMcCZLNg9WWLEONKPWNBvrpLzemiI9fh0O35kndHnzXL+501eA41uetOqYwpbwIGxECxKnQEZTtckhqQIDAQAB";

    /**
     * 数据解密公钥
     */
    private static String PRIVATE_KEY;

    public static void setPublicKey(String publicKey){
        PUBLIC_KEY = publicKey;
    }

    public static void setPrivateKey(String privateKey){
        PRIVATE_KEY = privateKey;
    }

    /**
     * 生成数据加密公钥和数据解密私钥
     * @return 数据加密公钥和解密私钥键值对
     * @throws NoSuchAlgorithmException 不匹配算法异常
     */
    public static Map<String, Key> generatePublicAndPrivateKey() {
        try {
            // 创建KeyPairGenerator（Java加密架构(JCA)中的一个类）实例，用于生成公钥和私钥
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA_ALGORITHM);
            // 初始化KeyPairGenerator实例，指定公钥和私钥长度，安全和性能综合考虑推荐2048
            keyPairGenerator.initialize(KEY_LENGTH);
            // 生成包含密钥对的实例
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            // 提取公钥
            Key publicKey = keyPair.getPublic();
            // 提取私钥
            Key privateKey = keyPair.getPrivate();
            // 创建一个初始容量位2的键值对，用于存储公私钥
            Map<String, Key> keyMap = new HashMap<>(2);
            // 将公钥放入键值对
            keyMap.put(RSA_PUBLIC_KEY, publicKey);
            // 将私钥放入键值对
            keyMap.put(RSA_PRIVATE_KEY, privateKey);
            return keyMap;
        } catch (Exception e) {
            throw new ExternalException(ExternalException.ErrorCodeEnum.GENERATE_KEY_PAIR_FAILED, "描述: " + e.getMessage(), ReturnCodeEnum.RC422);
        }
    }

    /**
     * 获取公钥
     * @return 公钥字符串
     */
    public static String getPublicKey() {
        return PUBLIC_KEY;
    }

    /**
     * 获取私钥
     * @return 私钥字符串
     */
    private static String getPrivateKey() {
        return PRIVATE_KEY;
    }

    /**
     * 获取公钥
     * @param keyMap 公私钥键值对
     * @return 公钥字符串
     */
    public static String getPublicKey(Map<String, Key> keyMap) {
        // 从公私钥键值对中取出公钥
        Key publicKey = keyMap.get(RSA_PUBLIC_KEY);
        // 获取公钥字节数组
        byte[] publicKeyBytes = publicKey.getEncoded();
        // 使用Base64编码将公钥字节数组转换为字符串
        return Base64.getEncoder().encodeToString(publicKeyBytes);
    }

    /**
     * 获取私钥
     * @param keyMap 公私钥键值对
     * @return 私钥字符串
     */
    private static String getPrivateKey(Map<String, Key> keyMap) {
        Key privateKey = keyMap.get(RSA_PRIVATE_KEY);
        byte[] privateKeyBytes = privateKey.getEncoded();
        return Base64.getEncoder().encodeToString(privateKeyBytes);
    }

    /**
     * 公钥加密
     * @param needEncrypt 需要加密数据
     * @return 公钥加密后的字符串
     * @throws Exception 异常
     */
    public static String encryptByPublicKey(String needEncrypt){
        try {
            // 使用Base64编码将数据加密公钥转换为字节数组
            byte[] publicKeyBytes = Base64.getDecoder().decode(PUBLIC_KEY);
            // 使用数据加密公钥字符数组创建一个X509EncodedKeySpec（密钥规范：公钥的ASN.1编码的一种表示形式）实例
            KeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKeyBytes);
            // 创建KeyFactory（JCE框架中的一个类）实例，用于转换公钥和私钥
            KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
            // 从x509EncodedKeySpec（密钥规范：公钥的ASN.1编码的一种表示形式）中生成一个公钥
            PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
            // 创建Cipher实例，用于加密解密
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            // 初始化Cipher实例，设置加密模式并指定要使用的公钥
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            // 加密为字节数组
            byte[] encryptBytes = cipher.doFinal(needEncrypt.getBytes());
            // 使用Base64编码将加密后的数组转换为字符串
            return Base64.getEncoder().encodeToString(encryptBytes);
        } catch (Exception e) {
            throw new ExternalException(ExternalException.ErrorCodeEnum.ENCRYPT_WITH_PUBLIC_KEY_FAILED, "描述: " + e.getMessage(), ReturnCodeEnum.RC422);
        }
    }

    /**
     * 私钥解密
     * @param needDecrypt 需要解密数据
     * @return 私钥解密后的字符串
     * @throws Exception 异常
     */
    public static String decryptByPrivateKey(String needDecrypt) {
        try {
            // 使用Base64编码将数据加密私钥转换为字节数组
            byte[] privateBytes = Base64.getDecoder().decode(PRIVATE_KEY);
            // 使用数据解密私钥字符数组创建一个PKCS8EncodedKeySpec（密钥规范：私钥的ASN.1编码的一种表示形式）实例
            KeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateBytes);
            // 创建KeyFactory（JCE框架中的一个类）实例，用于转换公钥和私钥
            KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
            // 从pkcs8EncodedKeySpec（密钥规范：私钥的ASN.1编码的一种表示形式）中生成一个私钥
            PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
            // 创建Cipher实例，用于加密解密
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            // 初始化Cipher实例，设置解密模式并指定要使用的私钥
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            // 使用Base64编码将需要解密数据转换为字节数组
            byte[] needDecryptBytes = Base64.getDecoder().decode(needDecrypt);
            // 解密为字节数组
            byte[] decryptBytes = cipher.doFinal(needDecryptBytes);
            return new String(decryptBytes);
        } catch (Exception e) {
            throw new ExternalException(ExternalException.ErrorCodeEnum.DECRYPT_WITH_PRIVATE_KEY_FAILED, "描述: " + e.getMessage(), ReturnCodeEnum.RC422);
        }
    }

    /**
     * 生成数据加密公钥和数据解密私钥后，将值替换到常量PUBLIC_KEY和PRIVATE_KEY
     * @param args 启动参数
     * @throws Exception 异常
     */
    public static void main(String[] args) throws Exception {
//        Map<String, Key> keyMap = generatePublicAndPrivateKey();
//        String publicKey = getPublicKey(keyMap);
//        String privateKey = getPrivateKey(keyMap);
//        System.out.println("数据加密公钥：" + publicKey);
//        System.out.println("数据解密私钥：" + privateKey);

        // 替换公私钥后将上方代码注释，放开下面注释，测试加密解密功能
        String data = "root";
        String encrypt = encryptByPublicKey(data);
        System.out.println("公钥加密后的数据：" + encrypt);
//        String decrypt = decryptByPrivateKey(encrypt);
//        System.out.println("私钥解密后的数据：" + decrypt);
    }
}
